From 36e15065d8c42e9b49f7323560879a60759b9120 Mon Sep 17 00:00:00 2001 From: sunlian Date: Fri, 12 Jan 2024 09:59:27 +0800 Subject: [PATCH] player details Signed-off-by: sunlian --- .../entry/src/main/ets/pages/MainPage.ets | 28 +- .../entry/src/main/ets/view/PlayerBar.ets | 8 +- .../entry/src/main/ets/view/PlayerDetail.ets | 283 ++++++++++++++++++ .../src/main/ets/view/VideoRecommendItem.ets | 1 - .../src/main/ets/viewmodel/MainViewModel.ets | 11 +- .../base/media/ic_public_arrow_down_0.png | Bin 0 -> 287 bytes .../base/media/ic_public_play_last.png | Bin 0 -> 320 bytes .../base/media/ic_public_play_next.png | Bin 0 -> 342 bytes 8 files changed, 323 insertions(+), 8 deletions(-) create mode 100644 scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerDetail.ets create mode 100644 scenario/arkui/MyMusic/entry/src/main/resources/base/media/ic_public_arrow_down_0.png create mode 100644 scenario/arkui/MyMusic/entry/src/main/resources/base/media/ic_public_play_last.png create mode 100644 scenario/arkui/MyMusic/entry/src/main/resources/base/media/ic_public_play_next.png diff --git a/scenario/arkui/MyMusic/entry/src/main/ets/pages/MainPage.ets b/scenario/arkui/MyMusic/entry/src/main/ets/pages/MainPage.ets index 0db6109f..80620a8f 100644 --- a/scenario/arkui/MyMusic/entry/src/main/ets/pages/MainPage.ets +++ b/scenario/arkui/MyMusic/entry/src/main/ets/pages/MainPage.ets @@ -26,6 +26,7 @@ import curves from '@ohos.curves'; import PodCastHead from '../view/PodCastHead'; import Setting from '../view/Setting'; import PlayList from '../view/PlayList'; +import PlayerDetail from '../view/PlayerDetail'; /** * Main page @@ -42,6 +43,7 @@ struct MainPage { @State current: number = 1 @State isShowSidebar: boolean = false; @State isShowPlayList: boolean = false; + @State isShowPlayerDetail: boolean = false; @State maskOpacity: number = 0.5; aboutToAppear() { @@ -52,12 +54,20 @@ struct MainPage { } onBackPress() { - if (this.isShowPlayList || this.isShowSidebar) { + if (this.isShowPlayList ) { animateTo({ duration: 350 }, () => { - this.isShowSidebar = false; this.isShowPlayList = false; - } - ) + }) + return true; + } else if(this.isShowPlayerDetail) { + animateTo({ duration: 350 }, () => { + this.isShowPlayerDetail = false; + }) + return true; + } else if(this.isShowSidebar) { + animateTo({ duration: 350 }, () => { + this.isShowSidebar = false; + }) return true; } else { return false; @@ -145,10 +155,18 @@ struct MainPage { this.currentIndex = index; }) - PlayerBar({ isShowPlayList: $isShowPlayList }) + PlayerBar({ isShowPlayList: $isShowPlayList, isShowPlayerDetail: $isShowPlayerDetail }) .margin({ bottom: $r('app.float.mainPage_barHeight') }) .backgroundColor(Color.White) .width('100%') + if (this.isShowPlayerDetail) { + PlayerDetail({ isShowPlayList: $isShowPlayList, isShowPlayerDetail: $isShowPlayerDetail }) + .width('100%') + .height('100%') + .transition(TransitionEffect.OPACITY + .animation({ curve: curves.springMotion() }) + .combine(TransitionEffect.move(TransitionEdge.BOTTOM))) + } if (this.isShowSidebar || this.isShowPlayList) { Rect() .width('100%') diff --git a/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerBar.ets b/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerBar.ets index 5442c80c..94e5b18b 100644 --- a/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerBar.ets +++ b/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerBar.ets @@ -31,6 +31,7 @@ export default struct PlayerBar { @State currentTime: number = 0; // 当前时间 @State state: number = 0; // 播放状态 @Link isShowPlayList: boolean; + @Link isShowPlayerDetail: boolean; private playerManager: PlayerManager = AppStorage.get('playerManager') as PlayerManager; private innerEvent: emitter.InnerEvent = { eventId: 1, @@ -133,7 +134,7 @@ export default struct PlayerBar { .objectFit(ImageFit.Contain) .width('32vp') .height('32vp') - .margin({ right: $r('app.float.home_text_margin'),left:'8vp' }) + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) .onClick(() => { animateTo({ duration: 350 }, () => { this.isShowPlayList = true; @@ -142,6 +143,11 @@ export default struct PlayerBar { } .width('100%') .margin({ top: $r('app.float.home_list_margin') }) + .onClick(() => { + animateTo({ duration: 350 }, () => { + this.isShowPlayerDetail = true; + }) + }) Divider().strokeWidth(1).color($r('app.color.divider_gray')) diff --git a/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerDetail.ets b/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerDetail.ets new file mode 100644 index 00000000..9da4d580 --- /dev/null +++ b/scenario/arkui/MyMusic/entry/src/main/ets/view/PlayerDetail.ets @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import CommonConstants from '../common/constants/CommonConstants'; +import PlayerManager from '../manager/PlayerManager'; +import emitter from '@ohos.events.emitter'; +import mainViewModel from '../viewmodel/MainViewModel'; +import Logger from '../common/utils/Logger'; + +/** + * Setting tab content + */ +@Component +export default struct PlayerDetail { + private tag: string = 'PlayerDetail'; + private playerManager: PlayerManager = AppStorage.get('playerManager') as PlayerManager; + @State playingTitle: string = this.playerManager.getItem().title + @State playingArtist: string = this.playerManager.getItem().artist + @State playingImg: Resource = this.playerManager.getItem().img + @State durationTime: number = this.playerManager.getItem().time; // 总时长 + @State currentTime: number = 0; // 当前时间 + @State state: number = 0; // 播放状态 + @State rotateAngle: number = 0; + @State subCurrentIndex: number = 0; + @State contentSwitch: boolean = true; + @Link isShowPlayList: boolean; + @Link isShowPlayerDetail: boolean; + private tabsController: TabsController = new TabsController(); + private innerEvent: emitter.InnerEvent = { + eventId: 1, + priority: emitter.EventPriority.HIGH + }; + + @Builder + TabBuilder(title: string, index: number, selectedImg: Resource, normalImg: Resource) { + Column() { + Row() { + Text(title) + .fontSize('20fp') + .fontColor(this.subCurrentIndex === index ? $r('app.color.mainPage_selected') : $r('app.color.mainPage_normal')) + }.alignItems(VerticalAlign.Top) + + Image(this.subCurrentIndex === index ? selectedImg : normalImg) + .width($r('app.float.mainPage_baseTab_size')) + } + .height($r('app.float.mainPage_barHeight')) + .margin({ + top: $r('app.float.mainPage_padding'), + left: $r('app.float.mainPage_padding'), + right: $r('app.float.mainPage_padding') + }) + .onClick(() => { + this.subCurrentIndex = index; + this.tabsController.changeIndex(this.subCurrentIndex); + }) + } + + aboutToAppear() { + emitter.on(this.innerEvent, (eventData: emitter.EventData) => { + if (eventData !== undefined && eventData.data !== undefined) { + Logger.info(this.tag, 'state:' + eventData.data.state); + Logger.info(this.tag, 'currentTime:' + eventData.data.currentTime); + this.currentTime = eventData.data.currentTime; + if (this.state !== 1 && eventData.data.state === 1) { + this.playingTitle = this.playerManager.getItem().title; + this.durationTime = this.playerManager.getItem().time; + this.playingArtist = this.playerManager.getItem().artist; + this.playingImg = this.playerManager.getItem().img; + if (this.rotateAngle === 0) { + this.rotateAngle = 360; + } + } + if (eventData.data.state !== 1) { + this.rotateAngle = 0; + } + this.state = eventData.data.state; + } + }); + } + + aboutToDisappear() { + //emitter.off(1); + } + + build() { + Column() { + Row() { + Image($r('app.media.ic_public_arrow_down_0')) + .height('32vp') + .width('32vp') + .onClick(() => { + animateTo({ duration: 350 }, () => { + this.isShowPlayerDetail = false; + }) + }) + Column() { + Text(this.playingTitle).fontSize('18fp') + Row() { + Text(this.playingArtist).fontSize('14fp') + .fontColor('#a0a0a0') + Text('关注') + .fontSize('14fp') + .borderRadius('8vp') + .backgroundColor('#22a0a0a0') + .margin('4vp') + .padding({ right: '4vp', left: '4vp' }) + Image($r('app.media.ic_public_arrow_right_grey')) + .width('8vp') + .height('16vp') + } + } + .width('70%') + + Image($r('app.media.ic_public_share')) + .height('32vp') + .width('32vp') + } + .width('100%') + .justifyContent(FlexAlign.SpaceEvenly) + + Stack() { + if (this.contentSwitch) { + Column() { + Image(this.playingImg) + .width('300vp') + .height('300vp') + .borderRadius('200vp') + .margin('50vp') + .rotate({ angle: this.rotateAngle }) + .animation({ + duration: 3600, + curve: Curve.Linear, + delay: 500, + iterations: -1, // 设置-1表示动画无限循环 + playMode: PlayMode.Normal + }) + }.width('100%') + .height('100%') + .onClick(() => { + this.contentSwitch = false; + }) + } else { + Column() { + Tabs({ + barPosition: BarPosition.End, + index: 0, + controller: this.tabsController + }) { + TabContent() { + Column() { + Scroll() { + Column({ space: CommonConstants.COMMON_SPACE }) { + } + } + .width('100%') + }.height('100%') + } + .tabBar(this.TabBuilder('歌词', 0, + $r('app.media.ic_screenshot_line_select'), $r('app.media.ic_screenshot_line'))) + + TabContent() { + Column() { + Scroll() { + Column({ space: CommonConstants.COMMON_SPACE }) { + } + } + .width('100%') + }.height('100%') + } + .tabBar(this.TabBuilder('百科', 1, + $r('app.media.ic_screenshot_line_select'), $r('app.media.ic_screenshot_line'))) + } + .width(CommonConstants.FULL_PARENT) + .barHeight($r('app.float.mainPage_barHeight')) + .barWidth('70%') + .barMode(BarMode.Scrollable) + .barPosition(BarPosition.Start) + .onChange((index: number) => { + this.subCurrentIndex = index; + }) + } + .width('100%') + .height('100%') + .onClick(() => { + this.contentSwitch = true; + if (this.state === 1) { + this.rotateAngle = 0; + animateTo({ duration: 3600 }, ()=>{ + this.rotateAngle = 360; + }) + } + }) + } + }.width('100%') + .height('70%') + + Row() { + Progress({ value: this.currentTime, total: this.durationTime, type: ProgressType.Linear }) + .width('80%') + .height(30) + }.width('100%') + .justifyContent(FlexAlign.Center) + + Row() { + Text(mainViewModel.formatTime(this.currentTime)).fontSize('12fp') + Text('无损').fontSize('12fp') + Text(mainViewModel.formatTime(this.durationTime)).fontSize('12fp') + }.width('80%') + .justifyContent(FlexAlign.SpaceBetween) + + Row() { + Image($r('app.media.ic_public_list_cycle')) + .objectFit(ImageFit.Contain) + .width('32vp') + .height('32vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + Image($r('app.media.ic_public_play_last')) + .objectFit(ImageFit.Contain) + .width('32vp') + .height('32vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + .onClick(() => { + this.playerManager.previous(); + }) + if (this.state === 1) { + Image($r('app.media.ic_public_pause')) + .objectFit(ImageFit.Contain) + .width('42vp') + .height('42vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + .onClick(() => { + this.playerManager.pause(); + }) + } else { + Image($r('app.media.ic_public_play')) + .objectFit(ImageFit.Contain) + .width('42vp') + .height('42vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + .onClick(() => { + this.playerManager.play(); + }) + } + Image($r('app.media.ic_public_play_next')) + .objectFit(ImageFit.Contain) + .width('32vp') + .height('32vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + .onClick(() => { + this.playerManager.next(); + }) + Image($r('app.media.ic_public_view_list')) + .objectFit(ImageFit.Contain) + .width('32vp') + .height('32vp') + .margin({ right: $r('app.float.home_text_margin'), left: '8vp' }) + .onClick(() => { + animateTo({ duration: 350 }, () => { + this.isShowPlayList = true; + }) + }) + }.width('100%') + .justifyContent(FlexAlign.SpaceEvenly) + } + .justifyContent(FlexAlign.SpaceEvenly) + .width('100%') + .height('100%') + .backgroundColor(Color.White) + .borderRadius({ topLeft: '18vp', topRight: '18vp' }) + } +} diff --git a/scenario/arkui/MyMusic/entry/src/main/ets/view/VideoRecommendItem.ets b/scenario/arkui/MyMusic/entry/src/main/ets/view/VideoRecommendItem.ets index 51c15e78..79b7f58a 100644 --- a/scenario/arkui/MyMusic/entry/src/main/ets/view/VideoRecommendItem.ets +++ b/scenario/arkui/MyMusic/entry/src/main/ets/view/VideoRecommendItem.ets @@ -118,7 +118,6 @@ export default struct VideoRecommendItem { Logger.error('onVisibleAreaChange:', this.item.name + 'stop') this.controller.stop(); } - }) } } \ No newline at end of file diff --git a/scenario/arkui/MyMusic/entry/src/main/ets/viewmodel/MainViewModel.ets b/scenario/arkui/MyMusic/entry/src/main/ets/viewmodel/MainViewModel.ets index ce7fa9a8..6d645592 100644 --- a/scenario/arkui/MyMusic/entry/src/main/ets/viewmodel/MainViewModel.ets +++ b/scenario/arkui/MyMusic/entry/src/main/ets/viewmodel/MainViewModel.ets @@ -48,6 +48,15 @@ export class MainViewModel { new AudioItem('Rylynn', $r('app.media.quaregreen5'), 'Andy McKee', 212) ] + formatTime(time: number): string { + let min = Math.floor(time / 60).toString(); + let sec = (time % 60).toString() + if (sec.length === 1) { + sec = '0' + sec; + } + return min + ':' + sec; + } + generateList(num: number): AudioItem[] { if (num > this.audios.length) { num = this.audios.length; @@ -648,6 +657,7 @@ export class MainViewModel { ]; return listData; } + getListHistoryData(): Array { let list: ListHistoryItemData[] = [ new ListHistoryItemData('歌单:时空之力', $r('app.media.top'), '', this.generateList(4)), @@ -658,7 +668,6 @@ export class MainViewModel { ]; return list; } - } export default new MainViewModel(); \ No newline at end of file diff --git a/scenario/arkui/MyMusic/entry/src/main/resources/base/media/ic_public_arrow_down_0.png b/scenario/arkui/MyMusic/entry/src/main/resources/base/media/ic_public_arrow_down_0.png new file mode 100644 index 0000000000000000000000000000000000000000..284284f35b9b6514d3c7a3341b1fc16fd2c481f7 GIT binary patch literal 287 zcmV+)0pR|LP)Px#*-1n}R7gwh)3HheK@^4IZwrw?o=31s<1?g*u~D!Jq>w;T*ad_XY3u|GQLL;4 z!H3Cv*r@|q2rIiYv#?;X0|QgcIsd)q&J;RcMaTYnKs4b#qW>My4llTklzD_Vd|_O@ z+ce}gR=C5m26BvdoMD3396-3c#T@sTRm1~)V1((u+dp-rJkt{LfNOkXh|k}BiYq+U z#h3yJ+6`_HAd9{E2`-z=gD@3v7*{xlE*dK@JIVnB%>~Z!gy-ld$m}4+uho!S8Mlon l8Px#`bk7VR7gwh)H_N9K@^4IZ-fa=bp^Tr1<}Y*L1SZ$O*9ih&`3=bbQUn_>_!lG zU?yfJqWHKl!q_xjW!argK_|uO^WR6^+tcysb?k2nNLILw5{1Vq?(tG3)&T~1$3ZeY zmT`+UJYflslmFELJ`vwc17Hpp*sExcaG3)LO&z>H&g=xUH36J`-<_5MwsD1phRlip z^B7_?qbEgxX!?GJc}R)?BkXhuSi}uBG9X16{P=F+x{D@H2Hwgu{TMr@1}z3^w^Fua zoTug>UdPx$5J^NqR7gwh)jdkYP!z@S-;@?wS-XJ&!4;?r&<)5Gf{m!4jf#yjBDT5#bq|78 zeijOX*lS@gR@w@h2N?+=$rC0ign3oMyE*^dlXG8M8eXl2^$h?~hs!J)K^yl`g-AgW zU<{8KO#v(EeU;P#W-*7)Y+(^#6Z<)}W31!7gmQ%`OyV{M*ui#z&)C%b7o+5bvmcDhcrVW&i*H07*qoM6N<$f^vwG*#H0l literal 0 HcmV?d00001 -- Gitee