#DAYU200体验官# 起司播客App 原创 精华

发布于 2022-7-6 18:35
浏览
1收藏

@toc

主题

本帖使用Dayu200为开发板,展示一个在线播音App - 起司播客。

预览效果图

#DAYU200体验官# 起司播客App-开源基础软件社区

#DAYU200体验官# 起司播客App-开源基础软件社区

#DAYU200体验官# 起司播客App-开源基础软件社区

Dayu200的预览配置

为了大幅提高UI的开发效率,降低Dayu200的使用门槛,在开发过程中,强烈建议使用DevEco Studio 3.0 Beta3(OpenHarmony)的MatePadPro作为预览配置,并调整到竖屏模式,最终与Dayu200上的效果近似一致。

资源导入

本案例为了简单起见,文字与颜色直接写在代码中,仅图片资源需要导入,将全部所需图片拖到pages的新建img子目录中:
#DAYU200体验官# 起司播客App-开源基础软件社区

首页结构

使用默认的index.ets入口页作为启动页,分析页面的结构,可以一个Column,从上至下依次是导航栏、分类标题、分类卡片列表、筛选栏、播客作品列表。

导航栏

导航栏的左侧是一个按列布局的两行文字,右侧是一个头像:

Row {

        Column {

          Text('起司播客')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)

          Text('爱情,生活,舒缓')
            .fontSize(15)
            .fontColor('#A3A1AF')

        }
	.alignItems(HorizontalAlign.Start)

        Blank()

        Image($r("app.media.profile"))
          .objectFit(ImageFit.Contain)
          .width(49)
          .height(49)

      }
      .width('100%')
      .padding({left:20,top:20,bottom:10,right:20})

#DAYU200体验官# 起司播客App-开源基础软件社区

分类标题

标题是一行文字,左对齐:

Row {

        Text('分类')
          .fontSize(20)
          .fontColor('#1E3354')

      }
      .width('100%')
      .padding({left:20,top:10,bottom:10,right:20})

分类卡片列表

定义分类列表的数据:

var cate = [
    {
      title: '音乐 & 娱乐',
      img: '/pages/img/cate1.png',
      total: '84',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '生活 & 舒缓',
      img: '/pages/img/cate2.png',
      total: '96',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '教育 & 学习',
      img: '/pages/img/cate3.png',
      total: '72',
      album: ['', '', '', '', '', '', ''],
    },
  ]

专辑数据:

var albums = [
    {
      title: 'Ngobam',
      cate: '音乐 & 娱乐',
      tag: 'pop',
      img: '/pages/img/item1.png',
      eps: '84',
      artist: 'Gofar Hilman'
    },
    {
      title: 'Semprod',
      cate: '生活 & 舒缓',
      tag: 'pop',
      img: '/pages/img/item2.png',
      eps: '44',
      artist: 'Kugo娱乐'
    },
    {
      title: 'Sruput Nendang',
      cate: '教育 & 学习',
      tag: 'pop',
      img: '/pages/img/item3.png',
      eps: '46',
      artist: 'Macro & Marlo'
    },
  ]

卡片本身由背景层和卡片文字组成。
背景层:

Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

        }
              .width('100%')
              .height('100%')

卡片文字又上下排列的Column组成:

Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "个播客   ")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')

#DAYU200体验官# 起司播客App-开源基础软件社区

将背景层和卡片文字组合起来,加上用户点击的互动:

Stack {

              Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

              }
              .width('100%')
              .height('100%')

              Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "个播客   ")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }
		.padding( left: 20 )
            .width(224)
            .height(296)
            .onClick( {() =>
              router.push(
                uri: 'pages/channel',
                params: {
                  place: item
                }
              )
            })

#DAYU200体验官# 起司播客App-开源基础软件社区

使用横向滑动的List组件和ForEach对卡片数据进行循环:

List {

        ForEach(this.cate, item => {

          ListItem {

            Stack {

              Column {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)

              }
              .width('100%')
              .height('100%')

              Column {

                Blank()

                Column {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)

                  Text(item.total + "个播客   ")
                    .fontSize(15)
                    .opacity(0.4)
                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }.padding( left: 20 )
            .width(224)
            .height(296)
            .onClick( () =>{
              router.push(
                uri: 'pages/channel',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(296)

#DAYU200体验官# 起司播客App-开源基础软件社区

筛选栏

定义筛选栏的数据数组和对应的索引数组:

var filters: string[] = [
    "流行", "最近", "音乐", "舒缓", "R&B"
  ]
  var filterIndices: number[] = this.filters.map{(_, index) => index}

定义用户选中的筛选栏状态变量:

@State var selected: number = 0

筛选栏是一系列的自定义按钮。单个按钮由图标加文字组成,第一个流行按钮有带火的图标。筛选栏中如果任何一个按钮被用户点击,则显示按钮的背景以及文字变粗体:

Button {

              Row {

                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }

                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick( () =>{
              this.selected = index
            })

#DAYU200体验官# 起司播客App-开源基础软件社区
将筛选按钮数据使用横向滑动的List和ForEach进行循环渲染,即可得到按钮组:

List( {space: 10} ) {

        ForEach(this.filterIndices,  index =>{

          ListItem {

            Button {

              Row {

                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }

                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick( () =>{
              this.selected = index
            })
          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(90)
      .padding(20)

#DAYU200体验官# 起司播客App-开源基础软件社区

#DAYU200体验官# 起司播客App-开源基础软件社区

音乐列表

单个的音乐条目由专辑图标、音乐名和作者、所属分类和专辑数目组合在一行之内。
圆角的专辑图标:

Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)

#DAYU200体验官# 起司播客App-开源基础软件社区

音乐名和作者:

Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

#DAYU200体验官# 起司播客App-开源基础软件社区

所属分类和专辑数目:

Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "个Ep")
                    .fontSize(15)
                    .opacity(0.4)

   }
                .width('90%')

#DAYU200体验官# 起司播客App-开源基础软件社区

将三个部分依次组合起来:

Row {

                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)

              Column {

                Blank()

                Row {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "个Ep")
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)

#DAYU200体验官# 起司播客App-开源基础软件社区

有了单个音乐条目的实现,就可以使用一个纵向滑动的List和ForEach循环渲染,再加上用户点击跳转到音乐播放页的互动:

List {

        ForEach(this.albums, item =>{

          ListItem {

            Row {

              Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .width(56)
                  .height(56)
                  .borderRadius(20)

              Column {

                Blank()

                Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding(left:10,right:10)

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Row {

                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding(left:5,right:5)

                  Text(item.eps + "个Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')
                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)
            .onClick( () =>{
              router.push(
                uri: 'pages/detail',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .width('100%')
      .height('35%')

#DAYU200体验官# 起司播客App-开源基础软件社区

完整代码和效果

将上述代码从上到下依次放入Column中,组成首页index.ets的完整代码:


import router from '@system.router';

@Entry
@Component
struct Index {
  @State selected: number = 0
  filters: string[] = [
    "流行", "最近", "音乐", "舒缓", "R&B"
  ]
  filterIndices: number[] = this.filters.map((_, index) => index)
  cate = [
    {
      title: '音乐 & 娱乐',
      img: '/pages/img/cate1.png',
      total: '84',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '生活 & 舒缓',
      img: '/pages/img/cate2.png',
      total: '96',
      album: ['', '', '', '', '', '', ''],
    },
    {
      title: '教育 & 学习',
      img: '/pages/img/cate3.png',
      total: '72',
      album: ['', '', '', '', '', '', ''],
    },
  ]
  albums = [
    {
      title: 'Ngobam',
      cate: '音乐 & 娱乐',
      tag: 'pop',
      img: '/pages/img/item1.png',
      eps: '84',
      artist: 'Gofar Hilman'
    },
    {
      title: 'Semprod',
      cate: '生活 & 舒缓',
      tag: 'pop',
      img: '/pages/img/item2.png',
      eps: '44',
      artist: 'Kugo娱乐'
    },
    {
      title: 'Sruput Nendang',
      cate: '教育 & 学习',
      tag: 'pop',
      img: '/pages/img/item3.png',
      eps: '46',
      artist: 'Macro & Marlo'
    },
  ]

  build() {
    Column() {
      Row() {

        Column() {
          Text('起司播客')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
          Text('爱情,生活,舒缓')
            .fontSize(15)
            .fontColor('#A3A1AF')
        }.alignItems(HorizontalAlign.Start)

        Blank()
        Image($r("app.media.profile"))
          .objectFit(ImageFit.Contain)
          .width(49)
          .height(49)
      }
      .width('100%')
      .padding({left:20,top:20,bottom:10,right:20})

      Row() {
        Text('分类')
          .fontSize(20)
          .fontColor('#1E3354')
      }
      .width('100%')
      .padding({left:20,top:10,bottom:10,right:20})

      List() {
        ForEach(this.cate, item => {
          ListItem() {
            Stack() {
              Column() {
                Image(item.img)
                  .objectFit(ImageFit.Cover)
                  .borderRadius(20)
              }
              .width('100%')
              .height('100%')

              Column() {
                Blank()
                Column() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                    .fontWeight(FontWeight.Bold)
                  Text(item.total + "个播客   ").fontSize(15).opacity(0.4)
                }
                .borderRadius(20)
                .alignItems(HorizontalAlign.Start)
                .backgroundColor(Color.White)
                .opacity(0.6)
                .backdropBlur(8)
                .padding(20)
                .width('100%')
              }
              .alignItems(HorizontalAlign.Start)
              .width('100%')
              .height('100%')
            }.padding({ left: 20 })
            .width(224)
            .height(296)
            .onClick(() => {
              router.push({
                uri: 'pages/channel',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(296)

      List({ space: 10 }) {
        ForEach(this.filterIndices, index => {
          ListItem() {
            Button() {
              Row() {
                if (index == 0) {
                  Image($r("app.media.fire"))
                    .objectFit(ImageFit.Contain)
                    .width(16).height(16)
                    .margin({ right: 10 })
                }
                Text(this.filters[index])
                  .fontSize(17)
                  .fontWeight(this.selected == index ? FontWeight.Bold : FontWeight.Lighter)
                  .fontColor(this.selected == index ? '#413E50' : '#A3A1AF')
              }.padding(15)

            }
            .type(ButtonType.Normal)
            .backgroundColor(this.selected == index ? '#EDF0FC' : Color.White)
            .borderRadius(10)
            .height(50)
            .onClick(() => {
              this.selected = index
            })
          }
        })
      }
      .listDirection(Axis.Horizontal)
      .width('100%')
      .height(90)
      .padding(20)

      List() {
        ForEach(this.albums, item => {
          ListItem() {
            Row() {
              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column() {
                Blank()
                Row() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding({left:10,right:10})
                  Text(item.artist).fontSize(15).opacity(0.4)
                }
                .width('90%')

                Row() {
                  Text(item.cate)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·").fontSize(15).opacity(0.2).padding({left:5,right:5})
                  Text(item.eps + "个Ep").fontSize(15).opacity(0.4)
                }
                .width('90%')
                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({left:20,top:10,bottom: 10, right:20})
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() => {
              router.push({
                uri: 'pages/detail',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .width('100%')
      .height('35%')
    }
    .width('100%')

  }
}

#DAYU200体验官# 起司播客App-开源基础软件社区

频道页

频道页是一个播客作者的详细介绍,结构的上半部分是播客信息区域(导航栏、头像、昵称、播客描述、作品数和昵称),下半部分是播客作品页。在pages下新建一个channel.ets源文件。

播客作品数据

播客个人信息:

var author = {
    desc: '听我用音乐娓娓道来',
    albums: 256,
    name: '奥珍妮博士',
    avatar: '/pages/img/uper_avatar1.png'
  }

播客作品信息:

var albums = [
    {
      title: '工作和生活之间',
      img: '/pages/img/ep1.png',
      duration: '56:38',
      eps: '56',
    },
    {
      title: '前进的力量',
      img: '/pages/img/ep2.png',
      duration: '28:01',
      eps: '35',
    }, {
      title: '让我惊喜的小猴',
      img: '/pages/img/ep3.png',
      duration: '1:40:20',
      eps: '42',
    }, {
      title: '我的爱情被疫情阻隔',
      img: '/pages/img/ep4.png',
      duration: '1:05:13',
      eps: '51',
    }, {
      title: '你为什么要振作起来?',
      img: '/pages/img/ep5.png',
      duration: '45:28',
      eps: '77',
    },
  ]

导航栏

导航栏左侧有一个返回按钮,中间是标题,右侧留空:

Row {

        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20)
          .height(20)
          .onClick(()=>{
            router.back()
          })

        Blank()

        Text('播客')
          .fontSize(20)

        Blank()

      }
	.width('100%')
      .padding( {left: 20, top: 20, bottom: 10, right: 20} )

#DAYU200体验官# 起司播客App-开源基础软件社区

播客个人信息区域

头像、昵称、播客描述、作品数和昵称都组合在一个Column中:

Column({space:15}) {

        Image(this.author.avatar)
          .objectFit(ImageFit.Contain)
          .width(84)
          .height(84)
          .borderRadius(21)

        Text(this.author.desc)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)

        Row {

          Text(this.author.albums + "个Ep")
            .fontSize(15)
            .opacity(0.4)

          Text("|")
            .fontSize(15)
            .opacity(0.2)
            .padding( left: 5, right: 5 )

          Text(this.author.name)
            .fontSize(15)
            .fontColor('#A3A1AF')

        }

        Button {

          Row {

            Image($r("app.media.follow"))
              .objectFit(ImageFit.Contain)
              .width(24)
              .height(24)
              .margin({right:10})

            Text('关注')
              .fontSize(18)
              .fontColor(Color.White)
          }
        }
        .type(ButtonType.Normal)
        .backgroundColor('#2882F1')
        .width(200)
        .height(48)
        .borderRadius(8)
      }
	.height('30%')

#DAYU200体验官# 起司播客App-开源基础软件社区

播客作品列表

列表有一个标题区:

Row {

        Text('全部EP')
          .fontSize(20)
          .fontColor('#1E3354')

      }
      .width('100%')
      .padding({left:20})

#DAYU200体验官# 起司播客App-开源基础软件社区
单个播客作品项目,由作品封面、作品名、时长和作品数,组合在一个Column中:

Row {

              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column {

                Blank()

                Row {

                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding( {left: 10, right: 10} )

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)
                }
                .width('90%')

                Row {

                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding( {left: 5, right: 5 })

                  Text(item.eps + "个Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Blank()

              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin( {left: 20, top: 10, bottom: 10, right: 20} )
            .padding(10)
            .width('90%')
            .height(72)

对于播客作品数据,使用List和ForEach循环渲染,加上用户互动跳转到播放页:

List {

        ForEach(this.albums, item =>{

          ListItem {

            Row {

              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column {

                Blank()

                Row {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)

                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding( {left: 10, right: 10} )

                  Text(item.artist)
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Row {

                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)

                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding( left: 5, right: 5 )

                  Text(item.eps + "个Ep")
                    .fontSize(15)
                    .opacity(0.4)

                }
                .width('90%')

                Blank()

              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin( {left: 20, top: 10, bottom: 10, right: 20} )
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() =>{
              router.push(
                uri: 'pages/detail',
                params: {
                  place: item
                }
              )
            })

          }
        })
      }
      .width('100%')
      .height('55%')

#DAYU200体验官# 起司播客App-开源基础软件社区

完整页面代码和预览效果

将以上各个子组件和数据组合起来,channel.ets源文件整个页面的代码:

import router from '@system.router';

@Entry
@Component
struct Channel {
  author = {
    desc: '听我用音乐娓娓道来',
    albums: 256,
    name: '奥珍妮博士',
    avatar: '/pages/img/uper_avatar1.png'
  }
  albums = [
    {
      title: '工作和生活之间',
      img: '/pages/img/ep1.png',
      duration: '56:38',
      eps: '56',
    },
    {
      title: '前进的力量',
      img: '/pages/img/ep2.png',
      duration: '28:01',
      eps: '35',
    }, {
      title: '让我惊喜的小猴',
      img: '/pages/img/ep3.png',
      duration: '1:40:20',
      eps: '42',
    }, {
      title: '我的爱情被疫情阻隔',
      img: '/pages/img/ep4.png',
      duration: '1:05:13',
      eps: '51',
    }, {
      title: '你为什么要振作起来?',
      img: '/pages/img/ep5.png',
      duration: '45:28',
      eps: '77',
    },
  ]

  build() {
    Column() {
      Row() {
        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick(()=>{
            router.back()
          })
        Blank()
        Text('播客')
          .fontSize(20)

        Blank()
      }.width('100%')
      .padding({ left: 20, top: 20, bottom: 10, right: 20 })

      Column({space:15}) {
        Image(this.author.avatar)
          .objectFit(ImageFit.Contain)
          .width(84).height(84).borderRadius(21)

        Text(this.author.desc)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)

        Row() {
          Text(this.author.albums + "个Ep").fontSize(15).opacity(0.4)

          Text("|").fontSize(15).opacity(0.2).padding({ left: 5, right: 5 })
          Text(this.author.name)
            .fontSize(15)
            .fontColor('#A3A1AF')
        }

        Button(){
          Row(){

            Image($r("app.media.follow"))
              .objectFit(ImageFit.Contain)
              .width(24).height(24).margin({right:10})
            Text('关注').fontSize(18).fontColor(Color.White)
          }
        }.type(ButtonType.Normal)
        .backgroundColor('#2882F1')
        .width(200).height(48).borderRadius(8)
      }.height('30%')


      Row() {
        Text('全部EP')
          .fontSize(20)
          .fontColor('#1E3354')
      }
      .width('100%')
      .padding({left:20})

      List() {
        ForEach(this.albums, item => {
          ListItem() {
            Row() {
              Image(item.img)
                .objectFit(ImageFit.Cover)
                .width(56).height(56)
                .borderRadius(20)

              Column() {
                Blank()
                Row() {
                  Text(item.title)
                    .fontSize(16)
                    .opacity(0.9)
                  Text("|")
                    .fontSize(15)
                    .opacity(0.05)
                    .padding({ left: 10, right: 10 })
                  Text(item.artist).fontSize(15).opacity(0.4)
                }
                .width('90%')

                Row() {
                  Text(item.duration)
                    .fontSize(16)
                    .opacity(0.4)
                  Text("·")
                    .fontSize(15)
                    .opacity(0.2)
                    .padding({ left: 5, right: 5 })
                  Text(item.eps + "个Ep").fontSize(15).opacity(0.4)
                }
                .width('90%')

                Blank()
              }
              .width('70%')
              .height('100%')
            }
            .borderRadius(18)
            .backgroundColor('#EDF0FC')
            .margin({ left: 20, top: 10, bottom: 10, right: 20 })
            .padding(10)
            .width('90%')
            .height(72)
            .onClick(() => {
              router.push({
                uri: 'pages/detail',
                params: {
                  place: item
                }
              })
            })

          }
        })
      }
      .width('100%')
      .height('55%')
    }
    .width('100%')
  }
}

#DAYU200体验官# 起司播客App-开源基础软件社区

播放页

播放页用于播放上一页的频道页的播客作品,包含导航栏、作品大图、作品名和作者、播放控制按钮。在pages目录下新建源文件detail.ets。

状态变量

播放的作品来自上一页,这里使用固定的数据。播放状态是用户可控的,使用@State变量,定义如下:

@State playing: boolean = false
  var music = {
    img: 'pages/img/ep1.png',
    author: '奥珍妮博士',
    title: '工作和生活之间',
    duration: '56:38',
  }

导航栏

导航栏左侧用于返回上一页,右侧可以将作品添加到播放列表:

Row {

        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick({()=>
            router.back()
          })

        Blank()

        Text(' ')

          .fontSize(20)

        Blank()

        Image($r("app.media.playlist"))
          .objectFit(ImageFit.Contain)
          .width(20)
          .height(20)

      }
      .width('100%')
      .padding(30)

#DAYU200体验官# 起司播客App-开源基础软件社区

作品大图

作品大图带有圆角和阴影,将Image放入Column中再修饰属性即可:

Column {

        Image(this.music.img)
          .objectFit(ImageFit.Cover)
          .width(279)
          .height(326)
          .borderRadius(16)

      }
      .borderRadius(16)
      .shadow({radius: 50, color: '#cfcfcf',
        offsetX:5, offsetY: 15})
      .margin(30)

#DAYU200体验官# 起司播客App-开源基础软件社区

作品名和作者

作品名和作者按列布局,其中作者的文字略带不透明效果:

Column {

        Text(this.music.title)
          .fontSize(20)

        Text(this.music.author)
          .fontSize(15)
          .opacity(0.4)

      }
      .padding(30)

#DAYU200体验官# 起司播客App-开源基础软件社区
播放控制按钮
播放按钮可以根据作品的播放状态来切换图标,用户点击按钮可以在播放或暂停两种状态进行切换:

Row {

        Image(this.playing ? $r("app.media.pause") : $r("app.media.play"))
          .objectFit(ImageFit.Cover)
          .width(64)
          .height(64)
          .onClick(()=>{
          	this.playing = !this.playing
          })
      }
	.padding(30)

#DAYU200体验官# 起司播客App-开源基础软件社区

#DAYU200体验官# 起司播客App-开源基础软件社区

完整页面源码和预览效果

将以上子组件和数据组合起来,detail.ets源文件整个页面的代码:

import router from '@system.router';

@Entry
@Component
struct Detail {
  @State playing: boolean = false
  music = {
    img: 'pages/img/ep1.png',
    author: '奥珍妮博士',
    title: '工作和生活之间',
    duration: '56:38',
  }

  build() {
    Column() {
      Row() {
        Image($r("app.media.back"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
          .onClick(()=>{
            router.back()
          })
        Blank()
        Text(' ')
          .fontSize(20)
        Blank()
        Image($r("app.media.playlist"))
          .objectFit(ImageFit.Contain)
          .width(20).height(20)
      }.width('100%')
      .padding(30)

      Column() {
        Image(this.music.img)
          .objectFit(ImageFit.Cover)
          .width(279).height(326)
          .borderRadius(16)
      }
      .borderRadius(16)
      .shadow({radius: 50, color: '#cfcfcf',
        offsetX:5, offsetY: 15})
      .margin(30)

      Column() {
        Text(this.music.title)
          .fontSize(20)
        Text(this.music.author)
          .fontSize(15).opacity(0.4)
      }.padding(30)

      Row() {
        Image(this.playing ? $r("app.media.pause") : $r("app.media.play"))
          .objectFit(ImageFit.Cover)
          .width(64).height(64)
        .onClick(()=>{
          this.playing = !this.playing
        })
      }.padding(30)


    }
    .width('100%')
    .height('100%')
  }
}

#DAYU200体验官# 起司播客App-开源基础软件社区

总结

Dayu200不仅适合设备开发,更适合App开发,配合最新的DevEco Studio 3.0,即使您手头没有设备,也可以进行相对完善的UI开发大部分工作。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
Podcast.zip 39.87M 10次下载
4
收藏 1
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐