前言:
在UI方面,为了适应不同尺寸的设备(主要是sm,md,lg),使用Grid布局,尺寸默认为四种(xm,sm.md,lg),可以支持六种(xm,sm,md,lg,xl,xxl)。
1.在内容的音乐列表中,也用到了List组件的line组件,以父组件传递的breakpoints属性值来决定lines括号中的值,总体上就是先布局,然后再往里面写内容
在TS方面,单独声明了音乐信息类和操作类,以及单独做了一个假的数据库,和单独的播放列表(默认是所有音乐,没有实现增删改功能),以便点击音乐列表使底部的播放栏显示对应的音乐信息
效果
代码
viewmodel/Option
export default
class Option{
key:string
value:string
imgSrc:Resource
constructor(key:string,value:string,imgSrc:Resource) {
this.key = key
this.value = value
this.imgSrc = imgSrc
}
}
viewmodel/SongInfo
@Observed
export default class SongInfo{
name:string
singer:string
songCover:string|Resource
vipImgSrc:string|Resource
constructor(name:string,singer:string,songCover:string|Resource,vipImgSrc:string|Resource) {
this.name = name
this.singer = singer
this.songCover = songCover
this.vipImgSrc = vipImgSrc
}
}
database/Songinfos
import SongInfo from '../viewmodel/SongInfo'
let SongInfos: SongInfo[] = [
new SongInfo("不称职的天才", '王以太',$r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("不知道你为了什么而来", '江懿',$r('app.media.app_icon'), $r('app.media.wodeVIP')),
new SongInfo("谁能避开恋爱这事情", '张天赋/姚焯菲', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("大鱼", '周深', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("有我呢", '派派',$r('app.media.app_icon'), $r('app.media.wodeVIP')),
new SongInfo("不得不爱", '潘玮柏', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("雨下一整夜", 'ICE杨长青',$r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("散了,断了,疯了", '磨尖的粉笔', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("不识字的作词家", '不蓝', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("巨鹿", '华晨宇/田燚', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("很远的地方", '张学友', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("天意", '刘德华', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("坏杀手", '探长', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("但我正在想你", '王铮亮', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("青花瓷", '周杰伦', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("虹之间", '金贵晟', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("我好想你", '苏打绿', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("突然想起你", '林宥嘉', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("冬眠", '阿YueYue/刘兆宇', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
new SongInfo("主角", 'Mario', $r('app.media.app_icon'),$r('app.media.wodeVIP')),
]
export default SongInfos
database/PlayList
import SongInfo from '../viewmodel/SongInfo'
let playList:SongInfo[] = []
export default playList
pages/index
import MusicHeader from '../components/MuiscHeader'
import MusicContent from '../components/MusicContent'
import MusicPlayer from '../components/MusicPlayer'
import playList from '../datebase/PlayList'
import SongInfos from '../datebase/SongInfos'
@Entry
@Component
struct Index{
aboutToAppear(): void {
for(let i = 0;i<SongInfos.length;i++){
playList.push(SongInfos[i])
}
}
@Provide currentIndex:number = 0
build() {
Column(){
//header
MusicHeader()
//content
MusicContent()
//bottom player
MusicPlayer()
}
.width('100%')
.height('100%')
}
}
components/MusicHeader
@Component
export default struct MusicHeader{
@State color:string = '#e4eaf6'
build() {
GridRow(){
//left
GridCol({span:{sm:6,md:6,lg:4}}){
Row({space:8}){
Image($r('app.media.fanhui'))
.width(25)
Text('歌单')
}
.width('100%')
.height(80)
.backgroundColor('#e4eaf6')
.padding(8)
}
//right
GridCol({span:{sm:6,md:6,lg:8}}){
Row(){
Image($r('app.media.gengduo'))
.width(40)
}
.width('100%')
.height(80)
.justifyContent(FlexAlign.End)
.padding(8)
.backgroundColor(this.color)
}
}
.onBreakpointChange((breakpoints)=>{
if(breakpoints == 'sm'){
this.color = '#e4eaf6'
}
else{
this.color ='#f8f8f8'
}
})
}
}
components/MusicContent
import MusicList from './MusicList'
import MusicListCover from './MusicListCover'
@Component
export default
struct MusicContent {
@State breakpoints:string = 'sm'
build() {
Column(){
GridRow(){
GridCol({span:{sm:12,md:6,lg:4}}){
MusicListCover({breakpoints:this.breakpoints})
}
GridCol({span:{sm:12,md:6,lg:8}}){
MusicList({breakpoints:this.breakpoints})
}
}
.onBreakpointChange((breakpoints)=>{
this.breakpoints = breakpoints
})
}.layoutWeight(1)
}
}
components/MusicCover
import Option from '../viewmodel/Option'
@Component
export default
struct MusicListCover {
@Link breakpoints:string
@State coverHeight:number = 0
@State options:Option[] = [
new Option('点赞','999+',$r('app.media.xiaiaixindianzan')),
new Option('下载','下载',$r('app.media.xiazai')),
new Option('评论','评论',$r('app.media.comment')),
new Option('分享','分享',$r('app.media.fenxiang_m'))
]
@Builder Cover(){
Stack({alignContent:Alignment.BottomStart}){
Image($r('app.media.head'))
.width('100%')
.borderRadius(15)
.onAreaChange((oldArea,newArea)=>{
this.coverHeight = newArea.height as number
})
Text($r('app.string.collection_num'))
.fontColor(Color.White)
}
.padding(this.breakpoints==='sm'?10:0)
.width('100%')
}
@Builder TitleAndDescription(){
Column({space:8}){
Text($r('app.string.list_name'))
.fontSize(20)
.fontWeight(700)
Text($r('app.string.playlist_Introduction'))
}
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Start)
.width('100%')
.padding(this.breakpoints=='sm'?{left:10,right:20}:{})
.height(this.breakpoints == 'sm'?this.coverHeight:70)
.margin(this.breakpoints=='sm'?{}:{top:20,bottom:20})
}
@Builder Operations(){
Row(){
ForEach(this.options,(item:Option,index:number)=>{
Column({space:2}){
Image(item.imgSrc)
.width(24)
Text(item.value)
}
})
}
.margin({top:20,bottom:30})
.width('100%')
.justifyContent(this.breakpoints=='sm'?FlexAlign.SpaceAround:FlexAlign.SpaceBetween)
}
build() {
Column(){
GridRow(){
GridCol({span:{sm:4,md:10},offset:{sm:0,md:1}}){
//Cover
this.Cover()
}
GridCol({span:{sm:8,md:10},offset:{sm:0,md:2}}){
//title and description
this.TitleAndDescription()
}
GridCol({span:{sm:12,md:10},offset:{sm:0,md:2}}){
//options
this.Operations()
}
}
}
.layoutWeight(this.breakpoints=='sm'?0:1)
.backgroundColor('#e4eaf6')
}
}
components/MusicList
import playList from '../database/PlayList'
import SongInfos from '../database/SongInfos'
import SongInfo from '../viewmodel/SongInfo'
@Component
export default struct MusicList {
//dateBase
@Consume currentIndex: number
@State songNum: number = SongInfos.length
@Link breakpoints: string
@Builder
playAll() {
Row({ space: 8 }) {
Image($r('app.media.music_top_player'))
.width(20)
.fillColor(Color.Red)
.onClick(() => {
this.currentIndex = 0
})
Text(`播放全部(${this.songNum})`)
.fontSize(20)
}
}
@Builder
orderAndSelectMore() {
Row({ space: 10 }) {
Image($r('app.media.music_orderButton'))
.width(20)
Image($r('app.media.music_selectMoreButton'))
.width(20)
}
}
@Builder
songItem(item: SongInfo,index:number) {
Row() {
Column({ space: 5 }) {
Text(item.name)
Row({ space: 3 }) {
Image(item.vipImgSrc)
.width(20)
Text(item.singer)
.fontSize(10)
}
.width('100%')
}
.alignItems(HorizontalAlign.Start)
.height('100%')
Image($r('app.media.music_songItem_more'))
.width(25)
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.padding(20)
.height(70)
.onClick(() => {
this.currentIndex = index
})
}
build() {
Column() {
//listHeader
Row() {
this.playAll()
this.orderAndSelectMore()
}
.justifyContent(FlexAlign.SpaceBetween)
.padding({
top: 30,
left: 20,
right: 20,
bottom: 30
})
.width('100%')
.borderRadius(20)
//songList
List({ space: 10 }) {
ForEach(SongInfos, (item: SongInfo, index) => {
if (SongInfos.length - 1) {
ListItem() {
this.songItem(item,index)
}
}
})
}
.divider({
strokeWidth: 1,
color: '#ccc',
startMargin: 20
})
.width('100%')
.layoutWeight(1)
.lanes(this.breakpoints == 'lg' ? 2 : 1)
}
}
}
components/MusicPlayer
import playList from '../database/PlayList'
import SongInfo from '../viewmodel/SongInfo'
@Component
export default
struct MusicPlayer {
@Consume currentIndex:number
@Builder leftSongInfo(){
Row(){
//SongCover
Image(playList[this.currentIndex].songCover)
.height('90%')
//songInfo
Column({space:8}){
Text(playList[this.currentIndex].name)
.fontSize(20)
Row(){
Image(playList[this.currentIndex].vipImgSrc)
.width(20)
Text(playList[this.currentIndex].singer)
.fontSize(10)
}
}.alignItems(HorizontalAlign.Start)
}
.layoutWeight(1)
.height('100%')
}
@Builder rightOption(){
Row(){
Image($r('app.media.music_top_player'))
.width(20)
.fillColor(Color.Red)
Image($r('app.media.bofangxiayishou'))
.width(20)
.fillColor(Color.Red)
.onClick(() => {
this.currentIndex +=1
this.currentIndex %= playList.length
})
Image($r('app.media.music_bottom_playList'))
.width(20)
.fillColor(Color.Red)
}
.justifyContent(FlexAlign.SpaceBetween)
.width(80)
.height('100%')
}
build() {
Row(){
//left:songInfo
this.leftSongInfo()
//right:options
this.rightOption()
}
.height('10%')
.width('100%')
.padding({top:10,left:20,right:20,bottom:10})
.justifyContent(FlexAlign.SpaceBetween)
}
}