Преглед изворни кода

实现网盘页面的自定义背景

实现Navidrome的滚动隐藏标题栏
onecold пре 8 месеци
родитељ
комит
1f9495ec7d

+ 33 - 8
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -20,6 +20,8 @@ import { ButtonFancyModifier,
 import { getRemoteDriveAccountLabel, getRemoteDriveDisplayLabel, getRemoteDrivePlaylistPrefix } from '../common/util/RemoteDriveLabel';
 import { RemoteDriveType } from '../common/enums/RemoteDriveType';
 import { Utility } from '../common/util/Utility';
+import { SettingPage } from './SettingPage';
+import { getCloudDiskIcon } from '../dialog/RemoteDriveAccountDialog';
 
 /**
  * 歌单播放事件数据
@@ -75,6 +77,11 @@ function decodeUrlEncodedString(encodedStr: string): string {
 @Entry
 @Component
 export struct WebDavMainPage {
+  @State isCustomizeBg: boolean = false //自定义背景界面
+  @StorageProp('isLandscape')  isLandscape: boolean = false;
+  @State blurValue: number = 0 //背景模糊
+  @State bgBrightness: number = 0 //背景亮度
+  @State customizeBgPath: string | undefined = '';
   private listScroller: ListScroller = new ListScroller()
   @StorageProp('bottomSafeHeight') bottomSafeHeight: number = 0;
   searchController: SearchController = new SearchController()
@@ -329,13 +336,24 @@ export struct WebDavMainPage {
     this.handleWebdavEvent(event);
   };
 
-
-  aboutToAppear(): void {
+  initSetting(){
+    this.isCustomizeBg  = PreferencesUtil.getBooleanSync(SettingPage.IS_CUSTOMIZE_BG, false)
+    this.customizeBgPath  = PreferencesUtil.getStringSync(SettingPage.IS_CUSTOMIZE_BG_PATH, '')
+    this.blurValue = PreferencesUtil.getNumberSync(SettingPage.CUSTOMIZE_BG_BLUR, 0)
+    this.bgBrightness = PreferencesUtil.getNumberSync(SettingPage.BG_BRIGHTNESS, 0)
     this.isShowFileName = PreferencesUtil.getBooleanSync('isShowFileName', false)
     this.isLongNameRoLL = PreferencesUtil.getBooleanSync('isLongNameRoLL', true)
     this.sortType = PreferencesUtil.getNumberSync('webDavSortType', 0)
     this.isNoJumpToHome = PreferencesUtil.getBooleanSync('isNoJumpToHome', true)
+  }
+  aboutToAppear(): void {
 
+    this.initSetting()
+    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    // 监听广播事件(通用设置配置更新)
+    emitter.on(eventSetting, (eventData: emitter.EventData) => {
+      this.initSetting()
+    });
 
     // 加载账户列表
     this.loadAccounts();
@@ -1134,7 +1152,7 @@ export struct WebDavMainPage {
       }
       .width('100%')
       .position({ top: 0, left: 0 })
-      .backgroundColor($r('app.color.start_window_background'))
+      // .backgroundColor($r('app.color.start_window_background'))
 
       // 多选操作条,位于播放条之上(仅在多选模式下显示)
       if (this.isMultiSelect || this.isDeletingSelection) {
@@ -1144,6 +1162,12 @@ export struct WebDavMainPage {
     .width('100%')
     .height('100%')
     .alignContent(Alignment.Bottom)
+    .backgroundImage(this.isCustomizeBg?this.customizeBgPath:$r('app.color.start_window_background'))
+    .backgroundImageSize(this.isLandscape?{width:'100%'}:{ height: '100%'})
+    .backgroundImagePosition(Alignment.Center)
+    .backdropBlur(this.blurValue)
+    .backgroundBrightness({rate:this.isCustomizeBg?0.1:0,lightUpDegree:this.bgBrightness})
+    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
   }
 
   @Builder
@@ -1154,7 +1178,8 @@ export struct WebDavMainPage {
       if (this.webdavManager.currentPath !== '') {
         Row({ space: 8 }) {
           Button({ type: ButtonType.Circle }) {
-            Image(this.webdavManager.canGoBack()?$r('app.media.back'):this.selectedAccount.coverPath)
+            Image(this.webdavManager.canGoBack()?$r('app.media.back'):
+              this.selectedAccount.coverPath?this.selectedAccount.coverPath:getCloudDiskIcon(this.selectedAccount.webType))
               .width(15)
               .height(15)
               .borderRadius(10)
@@ -1240,12 +1265,12 @@ export struct WebDavMainPage {
           .fontColor($r('app.color.index_tab_font_color'))
           .margin({ left: 12 })
       }
-      .padding({top: this.topSafeHeight + 90, bottom: 20, left: 20, right: 20})
+      .padding({top: this.topSafeHeight + 90, bottom: 5, left: 20, right: 20})
       .visibility(this.isLoading?Visibility.Visible:Visibility.None)
       .opacity(this.isLoading ? 1 : 0)
       .animation({
-        duration: 500,
-        curve: 'ease-in-out' // 可选动画曲线
+        duration: 800,
+        curve: Curve.Smooth // 可选动画曲线
       })
 
 
@@ -1273,7 +1298,7 @@ export struct WebDavMainPage {
           }, (item: VideoItem) =>  item.filePath + '_' + this.listRefreshKey)
         }
         .scrollBar(BarState.Off)
-        .contentStartOffset(this.topSafeHeight + 110)
+        .contentStartOffset(this.topSafeHeight + 85)
         .contentEndOffset(this.bottomSafeHeight+70)
         .layoutWeight(1)
         .margin({ top: 4 })

+ 37 - 105
entry/src/main/ets/view/LocalMusic.ets

@@ -3497,6 +3497,40 @@ export struct LocalMusic {
             }
             .layoutWeight(1)
             .height('100%')
+            //拖拽pc或者pad的用鼠标拖拽音乐和视频到List或Grid上自动导入视频
+            .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
+            .onDrop((event?: DragEvent) => {
+              try {
+                let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData;
+                if (dragData !== undefined) {
+                  let records: unifiedDataChannel.UnifiedRecord[] = dragData.getRecords();
+                  if (records.length > 0) {
+                    for (let i = 0; i < records.length; i++) {
+                      let types = records[i].getTypes();
+                      if (types.includes(uniformTypeDescriptor.UniformDataType.FILE_URI)) {
+                        const fileUriUds =
+                          records[i].getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI) as uniformDataStruct.FileUri;
+                        let typeDescriptor = uniformTypeDescriptor.getTypeDescriptor(fileUriUds.fileType);
+                        if (typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.AUDIO)
+                          ||typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.VIDEO)) {
+                          this.targetFile = fileUriUds.oriUri;
+                          this.saveVideoDatas([this.targetFile])
+                          hilog.info(0x0000, 'Heanup', '当前targetFile:' + this.targetFile);
+                        }
+                      }
+                    }
+                  } else {
+                    hilog.info(0x0000, TAG, `%{public}s`, `dragData arr is null`);
+                  }
+                } else {
+                  hilog.info(0x0000, TAG, `%{public}s`, `dragData is undefined`);
+                }
+
+              } catch (error) {
+                const err = error as BusinessError;
+                hilog.error(0x0000, TAG, `startDataLoading errorCode: ${err.code}, errorMessage: ${err.message}`);
+              }
+            })
             // 右侧居中的字母表索引
             this.indexBuilder()
 
@@ -5551,43 +5585,6 @@ export struct LocalMusic {
           return { offsetRemain: offset };
         })
 
-        //允许拖拽音乐和视频到List或Grid上自动导入视频
-        .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
-        .onDrop((event?: DragEvent) => {
-          try {
-            let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData;
-            if (dragData !== undefined) {
-              let records: unifiedDataChannel.UnifiedRecord[] = dragData.getRecords();
-              if (records.length > 0) {
-                for (let i = 0; i < records.length; i++) {
-                  let types = records[i].getTypes();
-                  if (types.includes(uniformTypeDescriptor.UniformDataType.FILE_URI)) {
-                    const fileUriUds =
-                      records[i].getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI) as uniformDataStruct.FileUri;
-                    let typeDescriptor = uniformTypeDescriptor.getTypeDescriptor(fileUriUds.fileType);
-                    if (typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.AUDIO)
-                      ||typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.VIDEO)) {
-                      this.targetFile = fileUriUds.oriUri;
-                      this.saveVideoDatas([this.targetFile])
-                      hilog.info(0x0000, 'Heanup', '当前targetFile:' + this.targetFile);
-                    }
-                  }
-                }
-              } else {
-                hilog.info(0x0000, TAG, `%{public}s`, `dragData arr is null`);
-              }
-            } else {
-              hilog.info(0x0000, TAG, `%{public}s`, `dragData is undefined`);
-            }
-
-          } catch (error) {
-            const err = error as BusinessError;
-            hilog.error(0x0000, TAG, `startDataLoading errorCode: ${err.code}, errorMessage: ${err.message}`);
-          }
-        })
-
-
-
 
       }
 
@@ -5945,40 +5942,7 @@ export struct LocalMusic {
 
       return { offsetRemain: offset };
     })
-    .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
-    .onDrop((event?: DragEvent) => {
-      try {
-        let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData;
-        if (dragData !== undefined) {
-          let records: unifiedDataChannel.UnifiedRecord[] = dragData.getRecords();
-          if (records.length > 0) {
-            for (let i = 0; i < records.length; i++) {
-              let types = records[i].getTypes();
-              if (types.includes(uniformTypeDescriptor.UniformDataType.FILE_URI)) {
-                const fileUriUds =
-                  records[i].getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI) as uniformDataStruct.FileUri;
-                let typeDescriptor = uniformTypeDescriptor.getTypeDescriptor(fileUriUds.fileType);
-                if (typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.AUDIO)
-                  ||typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.VIDEO)) {
-                  this.targetFile = fileUriUds.oriUri;
-                  this.saveVideoDatas([this.targetFile])
-                  hilog.info(0x0000, 'Heanup', '当前targetFile:' + this.targetFile);
-                }
-              }
-            }
-          } else {
-            hilog.info(0x0000, TAG, `%{public}s`, `dragData arr is null`);
-          }
-        } else {
-          hilog.info(0x0000, TAG, `%{public}s`, `dragData is undefined`);
-        }
-
-      } catch (error) {
-        const err = error as BusinessError;
-        hilog.error(0x0000, TAG, `startDataLoading errorCode: ${err.code}, errorMessage: ${err.message}`);
-      }
-    })
-
+    
 
   }
   @State targetFile: string = '';
@@ -6456,6 +6420,7 @@ export struct LocalMusic {
         .width(180)
       }
       .height('auto')
+      .enableScrollInteraction(true)
       .scrollBar(this.isGridMusic?BarState.On:BarState.Off)
     }
 
@@ -6809,40 +6774,7 @@ export struct LocalMusic {
       return { offsetRemain: offset };
     })
 
-    //拖拽pc或者pad的用鼠标拖拽音乐和视频到List或Grid上自动导入视频
-    .allowDrop([uniformTypeDescriptor.UniformDataType.AUDIO,uniformTypeDescriptor.UniformDataType.VIDEO])
-    .onDrop((event?: DragEvent) => {
-      try {
-        let dragData: UnifiedData = (event as DragEvent).getData() as UnifiedData;
-        if (dragData !== undefined) {
-          let records: unifiedDataChannel.UnifiedRecord[] = dragData.getRecords();
-          if (records.length > 0) {
-            for (let i = 0; i < records.length; i++) {
-              let types = records[i].getTypes();
-              if (types.includes(uniformTypeDescriptor.UniformDataType.FILE_URI)) {
-                const fileUriUds =
-                  records[i].getEntry(uniformTypeDescriptor.UniformDataType.FILE_URI) as uniformDataStruct.FileUri;
-                let typeDescriptor = uniformTypeDescriptor.getTypeDescriptor(fileUriUds.fileType);
-                if (typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.AUDIO)
-                  ||typeDescriptor.belongsTo(uniformTypeDescriptor.UniformDataType.VIDEO)) {
-                  this.targetFile = fileUriUds.oriUri;
-                  this.saveVideoDatas([this.targetFile])
-                  hilog.info(0x0000, 'Heanup', '当前targetFile:' + this.targetFile);
-                }
-              }
-            }
-          } else {
-            hilog.info(0x0000, TAG, `%{public}s`, `dragData arr is null`);
-          }
-        } else {
-          hilog.info(0x0000, TAG, `%{public}s`, `dragData is undefined`);
-        }
 
-      } catch (error) {
-        const err = error as BusinessError;
-        hilog.error(0x0000, TAG, `startDataLoading errorCode: ${err.code}, errorMessage: ${err.message}`);
-      }
-    })
 
   }
 
@@ -13604,7 +13536,7 @@ export struct LocalMusic {
     //启动预加载
     this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", "1");
     // 设置无缓冲,这是播放器的缓冲区,有数据就播放
-    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", "1");
+    this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", "0");
     //跳帧处理,放CPU处理较慢时,进行跳帧处理,保证播放流程,画面和声音同步
     this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", "5");
     // 最大缓冲cache是3s, 有时候网络波动,会突然在短时间内收到好几秒的数据

+ 69 - 10
entry/src/main/ets/view/NavidromePage.ets

@@ -22,6 +22,8 @@ import { setNavidromePlaylist } from '../common/util/NavidromePlaylistStore';
 import { navidromeApi, NavidromeSong } from '../common/network/NavidromeApi';
 import { ServerLogUtil } from '../common/util/ServerLogUtil';
 import { RemoteDriveManager } from '../common/util/RemoteDriveManager';
+import { SettingPage } from '../pages/SettingPage';
+
 const NAVIDROME_PLAYLIST_ID = 'navidrome-playlist';
 const NAVIDROME_SEARCH_LIMIT = 500;
 
@@ -47,6 +49,15 @@ interface CachePreviewInfo {
 
 @Component
 export struct NavidromePage {
+  @State isShowTitleBar: boolean = true //是否显示分类导航条
+  private scroller: Scroller = new Scroller()
+  private prevOffsetY: number = 0; // 记录上一次的Y轴偏移量
+  @State autoHideTitle: boolean = true //滚动自动隐藏标题栏
+  @State isCustomizeBg: boolean = false //自定义背景界面
+  @StorageProp('isLandscape')  isLandscape: boolean = false;
+  @State blurValue: number = 0 //背景模糊
+  @State bgBrightness: number = 0 //背景亮度
+  @State customizeBgPath: string | undefined = '';
   @Link mType: number;
   @Link offsetX: number;
   @Link isShowDrawer: boolean;
@@ -67,7 +78,7 @@ export struct NavidromePage {
   @State isAlbumPageLoading: boolean = false;
   @StorageProp('isDarkMode') isDarkMode: boolean = false;
   @StorageLink('currentSong') currentSong: VideoItem | undefined = undefined;
-  @StorageProp('topRectHeight') topRectHeight: number = 0;
+  @StorageProp('topSafeHeight') topSafeHeight: number = 0;
   @State @Watch('onTabSelectedIndexesChanged') tabSelectedIndexes: number[] = [0];
   @StorageProp('themeColor') themeColor: string =
     PreferencesUtil.getStringSync('THEME_COLOR', CommonConstants.DEFAULT_THEME_COLOR);
@@ -158,9 +169,25 @@ export struct NavidromePage {
     await this.refreshNavidromeData(true);
   }
 
-  aboutToAppear() {
+  initSetting(){
+    this.isCustomizeBg  = PreferencesUtil.getBooleanSync(SettingPage.IS_CUSTOMIZE_BG, false)
+    this.customizeBgPath  = PreferencesUtil.getStringSync(SettingPage.IS_CUSTOMIZE_BG_PATH, '')
+    this.blurValue = PreferencesUtil.getNumberSync(SettingPage.CUSTOMIZE_BG_BLUR, 0)
+    this.bgBrightness = PreferencesUtil.getNumberSync(SettingPage.BG_BRIGHTNESS, 0)
     this.isNoJumpToHome = PreferencesUtil.getBooleanSync('isNoJumpToHome', true)
     this.sortType = PreferencesUtil.getNumberSync('navidromeSortType', 0);
+    this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
+    this.autoHideTitle = PreferencesUtil.getBooleanSync('autoHideTitle', true)
+  }
+
+  aboutToAppear() {
+    this.initSetting()
+    let eventSetting: emitter.InnerEvent = { eventId: 333 }
+    // 监听广播事件(通用设置配置更新)
+    emitter.on(eventSetting, (eventData: emitter.EventData) => {
+      this.initSetting()
+    });
+
     this.refreshNavidromeData();
   }
 
@@ -803,7 +830,7 @@ export struct NavidromePage {
         .padding({ left: 25, right: 25, top: 5, bottom: 5 })
     }
     .width('100%')
-    .padding({ top: this.topRectHeight + 5 })
+    .padding({ top: this.topSafeHeight + 5 })
     // .backgroundColor($r('app.color.start_window_background'))
   }
 
@@ -1441,6 +1468,7 @@ export struct NavidromePage {
       }
       .width('100%')
       .layoutWeight(1)
+      .padding({top:this.topSafeHeight+98})
       .justifyContent(FlexAlign.Center)
     } else {
       if (this.selectedTab === 0 && this.filterType !== NavFilterType.None) {
@@ -1449,14 +1477,14 @@ export struct NavidromePage {
             .fontSize(13)
             .fontColor(this.themeColor)
             .layoutWeight(1)
-          Button('清除筛选')
+          Button('返回')
             .type(ButtonType.Capsule)
             .backgroundColor(this.themeColor)
             .fontSize(12)
             .onClick(() => this.clearFilter())
         }
         .width('90%')
-        .padding({ left: 16, right: 16, top: 12, bottom: 2 })
+        .padding({ left: 16, right: 16, top: this.topSafeHeight+118, bottom: 2 })
       }
 
       if (this.isSearchMode && this.selectedTab === 0 && this.searchText.length > 0 && this.isSearchLoading) {
@@ -1486,7 +1514,7 @@ export struct NavidromePage {
         .width('100%')
         .layoutWeight(1)
       } else {
-        List({ space: 8 }) {
+        List({scroller:this.scroller, space: 8 }) {
           if (this.selectedTab === 0) {
             ForEach(this.getVisibleSongs(), (item: VideoItem, index: number) => {
               ListItem() {
@@ -1507,12 +1535,32 @@ export struct NavidromePage {
             }, (album: NavidromeRestAlbum) => album.id)
           }
         }
+        .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 };
+        })
         .width('100%')
+        .height('100%')
         .layoutWeight(1)
         .cachedCount(3)
         .padding({ top: 5, bottom: 5 })
         .listDirection(Axis.Vertical)
-      .contentEndOffset(this.bottomSafeHeight+70)
+        .contentEndOffset(this.bottomSafeHeight+70)
+        .contentStartOffset(this.topSafeHeight + 98)
         .scrollBar(BarState.Auto)
         .edgeEffect(EdgeEffect.Spring)
         .onReachEnd(() => {
@@ -1549,18 +1597,29 @@ export struct NavidromePage {
 
     Stack() {
       Column() {
-        this.buildContentView();
+        this.buildContentView()
       }
-      .padding({top:this.topRectHeight+98})
-
+      .height('100%')
       Column() {
         this.topTitleBar()
       }
+      .visibility(this.isShowTitleBar?Visibility.Visible:
+        this.autoHideTitle?Visibility.None:Visibility.Visible)
+      .animation({
+        duration: 500,
+        curve: Curve.Friction  // 可选动画曲线
+      })
 
     }
     .alignContent(Alignment.Top)
     .width('100%')
     .height('100%')
+    .backgroundImage(this.isCustomizeBg?this.customizeBgPath:$r('app.color.start_window_background'))
+    .backgroundImageSize(this.isLandscape?{width:'100%'}:{ height: '100%'})
+    .backgroundImagePosition(Alignment.Center)
+    .backdropBlur(this.blurValue)
+    .backgroundBrightness({rate:this.isCustomizeBg?0.1:0,lightUpDegree:this.bgBrightness})
+    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
 
   }
 }