|
|
@@ -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() {
|